package com.ryan.member.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.Gson;
import com.ryan.exception.TravelException;
import com.ryan.member.entity.Member;
import com.ryan.member.entity.vo.LoginVo;
import com.ryan.member.entity.vo.RegisterVo;
import com.ryan.member.entity.vo.UpdatePwdVo;
import com.ryan.member.mapper.MemberMapper;
import com.ryan.member.service.MemberService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ryan.member.utils.ConstantWxUtil;
import com.ryan.member.utils.HttpClientUtils;
import com.ryan.utils.JwtUtil;
import com.ryan.utils.MD5;
import com.ryan.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 会员表 服务实现类
 * </p>
 *
 * @author ryan
 * @since 2020-07-29
 */
@Service
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements MemberService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private MemberService memberService;

    /**
     * 用户注册
     * @param registerVo
     * @return
     */
    @Override
    public int register(RegisterVo registerVo) {
        //获取用户输入的信息
        @NotNull String nickname = registerVo.getNickname();
        @NotNull String mobile = registerVo.getMobile();
        @NotNull String code = registerVo.getCode();
        @NotNull String password = registerVo.getPassword();

        //前后端都已经加了数据验证，不要再这里判断
        //首先判断验证码是否正确
        System.out.println(mobile);
        System.out.println(code);
        System.out.println(code.equals(redisTemplate.opsForValue().get(mobile)));
        System.out.println(redisTemplate.opsForValue().get(mobile));
        boolean equals = code.equals(redisTemplate.opsForValue().get(mobile));
        System.out.println(equals);
        if (!code.equals(redisTemplate.opsForValue().get(mobile))){
            throw new TravelException(20001, "验证码错误");
        }

        //判断手机号是否重复
        QueryWrapper<Member> memberWrapper = new QueryWrapper<>();
        memberWrapper.eq("mobile", mobile);
        Member member1 = baseMapper.selectOne(memberWrapper);
        if (member1 != null) throw new TravelException(20001, "该手机号已被注册，请登陆或者重新注册吧");

        //以上都没问题的话，将用户数据存到数据库即可
        Member member = new Member();
        member.setMobile(mobile);
        member.setNickname(nickname);
        member.setPassword(MD5.encrypt(password));
        member.setIsDisabled(false);
        member.setAvatar("https://ryan-travel.oss-cn-shenzhen.aliyuncs.com/default.jpg");//默认头像
        return baseMapper.insert(member);
    }


    /**
     * 登陆功能
     * @param loginVo
     * @return
     */
    @Override
    public String login(LoginVo loginVo) {
        //获取用户输入的信息
        @NotNull String mobile = loginVo.getMobile();
        @NotNull String password = loginVo.getPassword();
        //根据手机号判断
        QueryWrapper<Member> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile", mobile);
        Member member = baseMapper.selectOne(wrapper);
        if (member == null) throw new TravelException(20001, "该手机号尚未注册，请先注册用户哦");
        //如果不为空，再判断密码和状态
        if(!MD5.encrypt(password).equals(member.getPassword())) throw new TravelException(20001,"密码不正确");
        if(member.getIsDisabled()) throw new TravelException(20001, "该用户已被禁用，请联系管理员");
        //如果都没问题，则登陆成功，并返回token
        return JwtUtil.getJwtToken(member.getId(), member.getNickname());
    }

    /**
     * 微信登陆接口，获取二维码
     * @return
     */
    @Override
    public String getCode() {

        //微信开放平台授权bashUrl,连接是固定的，%s相当于sql语句中？占位符，其他的都是固定值，详细可以参考开发文档
        //注意下面拼接的redirect_uri二部是url
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        String redirectUrl = ConstantWxUtil.REDIRECT_URL;

        //对url进行utf-8编码
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        //对baseUrl中的参数赋值,并返回
        return String.format(
                baseUrl,
                ConstantWxUtil.APP_ID,
                redirectUrl,
                "ryan"
        );
    }

    /**
     * 微信扫码登陆成功后，获取jwtToken，并返回，带着token跳转到网站首页
     * @param code
     * @param status
     * @return
     */
    @Override
    public String callback(String code, String status) {
        try {
            //1 获取code值，临时票据，类似于验证码
            //2 拿着code请求 微信固定的地址，得到两个值 accsess_token 和 openid
            String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                    "?appid=%s" +
                    "&secret=%s" +
                    "&code=%s" +
                    "&grant_type=authorization_code";
            String accessTokenUrl = String.format(
                    baseAccessTokenUrl,
                    ConstantWxUtil.APP_ID,
                    ConstantWxUtil.APP_SECRET,
                    code
            );
            //请求这个拼接好的地址，得到返回两个值 accsess_token 和 openid
            //使用httpclient发送请求，得到返回结果；
            String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
            //先打印出来看看数据，再往下开发
            //System.out.println(accessTokenInfo);
            //主要得到了以下一些数据，其中最主要的是access_token和openid
            /*
            {"access_token":"35_Mlim9my1JvMMlxEAjdXl_myzYMqK557-gDMT1xPFzkdbuZtCcZhDKYF76fMNm2x4jezwjtDskajHz4moqU3QkY-8-UchF9h97pFfaXXxWDc",
            "expires_in":7200,
            "refresh_token":"35_WrtoTiyByt-WCteQkOK8QfC0uKgASo1TCr9xsDA_McuRwFP16r78PgIl4wpD325Vj5LcI_kGsejd7p-Q1_YPSkKb2JAtbGCiWR5q2flr64E",
            "openid":"o3_SC5y2N2fYnSLhnUp7l2qZpekM",
            "scope":"snsapi_login",
            "unionid":"oWgGz1CNae7qIKCKMuk3esyotkw0"}
             */
            //拿着返回的数据，使用httpclient进一步请求微信提供的地址
            //因为返回的是字符串，类似于key-value的形式，我们要怎么去获取呢？
            //可以先将返回的数据转换为map集合，然后通过key获取,这里使用json的转换工具为gson，
            Gson gson = new Gson();
            HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
            String access_token = (String) mapAccessToken.get("access_token");
            String openid = (String) mapAccessToken.get("openid");

            //在发送请求之前，我们先应该先通过openid判断该微信用户是否注册过
            Member member = memberService.getByOpenId(openid);
            if (member == null){
                //如果为空表示该微信用户还没注册过，则拿着access_token和openid继续请求微信提供的固定地址
                String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                        "?access_token=%s" +
                        "&openid=%s";
                //拼接两个参数
                String userInfoUrl = String.format(
                        baseUserInfoUrl,
                        access_token,
                        openid
                );
                String userInfo = HttpClientUtils.get(userInfoUrl);
                //查看下userInfo里面包含的数据
                //System.out.println(userInfo);
                /*
                {"openid":"o3_SC5y2N2fYnSLhnUp7l2qZpekM",
                "nickname":"牛排🍖",
                "sex":1,
                "language":"zh_CN",
                "city":"Guangzhou",
                "province":"Guangdong",
                "country":"CN",
                "headimgurl":"http:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/3akBBSAb7wJNJ124Z0PNicuNibKlUvefsiaPLfcwPUTxxdG1iaAwzylC148j4Q8nSc6jicZoKbpDdia2ldKwicISCF98Q\/132",
                "privilege":["chinaunicom"],
                "unionid":"oWgGz1CNae7qIKCKMuk3esyotkw0"}

                 */
                //获取到返回的用户信息之后，将其注册到数据库中，也就是member
                //注册前我们应该先将刚返回的userInfo转换为map集合
                HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
                String nickname = (String) userInfoMap.get("nickname");
                String avatar = (String) userInfoMap.get("headimgurl");
                member = new Member();
                member.setOpenid(openid);
                member.setNickname(nickname);
                member.setAvatar(avatar);
                memberService.save(member);
            }
            //获取到用户信息之后，我们怎么样才能在前端获取到这个用户信息呢？
            //有的人可能会想到将其存到cookie中，但是要主要cookie是没法跨域传递的，localhost倒是可以，但是在实际开发中肯定是不同的域名的
            //所以我们还是通过jwt生成token，并通过域名传递
            System.out.println("################获取token前");
            String jwtToken = JwtUtil.getJwtToken(member.getId(), member.getNickname());
            System.out.println("############获取token后" + jwtToken);
            //最后：返回首页面，通过路径传递token字符串
            return jwtToken;
        } catch (Exception e) {
            throw new TravelException(20001, "登陆失败");
        }
    }

    /**
     * 通过openid获取用户信息
     * @param openid
     * @return
     */
    @Override
    public Member getByOpenId(String openid) {
        QueryWrapper<Member> wrapper = new QueryWrapper<>();
        wrapper.eq("openid", openid);
        return baseMapper.selectOne(wrapper);
    }

    /**
     * 获取所有的用户列表
     * @param pageIndex
     * @param pageSize
     * @param name
     * @return
     */
    @Override
    public Page<Member> getMemberList(long pageIndex, long pageSize, String name) {
        QueryWrapper<Member> wrapper = new QueryWrapper<>();
        if(!StringUtils.isEmpty(name)){
            wrapper.eq("nickname",name);
        }
        wrapper.orderByDesc("gmt_create");
        Page<Member> page = new Page<>(pageIndex,pageSize);
        baseMapper.selectPage(page,wrapper);
        return page;
    }

    /**
     * 用户修改密码
     * @param updatePwdVo
     * @return
     */
    @Override
    public int updatePwd(UpdatePwdVo updatePwdVo) {
        //获取用户输入的信息
        String code = updatePwdVo.getCode();
        String mobile = updatePwdVo.getMobile();
        String password = updatePwdVo.getPassword();

        //非空判断
        if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)
                || StringUtils.isEmpty(code)) {
            throw new TravelException(20001,"请输入修改信息");
        }

        //判断验证码
        //获取redis验证码
        String redisCode = redisTemplate.opsForValue().get(mobile);
        if(!code.equals(redisCode)){
            throw new TravelException(20001,"验证码错误");
        }

        //如果以上数据都没有问题，则进行修改操作
        //首先根据手机号码获取用户信息
        QueryWrapper<Member> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile",mobile);
        Member member = baseMapper.selectOne(wrapper);
        if (member == null) throw new TravelException(20001,"该手机号尚未注册，请检查手机号信息是否有误");
        member.setPassword(MD5.encrypt(password));
        return baseMapper.updateById(member);
    }

    /**
     * 统计某个事件的注册人数
     * @param day
     * @return
     */
    @Override
    public Integer countRegister(String day) {
        return baseMapper.countRegister(day);
    }

}
